Java对比两个对象的不同,可对比基本类型、对象套对象、List 您所在的位置:网站首页 java数据对比 取出有差异的 Java对比两个对象的不同,可对比基本类型、对象套对象、List

Java对比两个对象的不同,可对比基本类型、对象套对象、List

2024-07-12 17:33| 来源: 网络整理| 查看: 265

Java对比两个对象的不同 JDK版本:1.8 (由于用到了lambda、stream) Maven

cn.hutool hutool-all 4.5.11 org.projectlombok lombok 1.18.8

假设我们现在有个需求,信息变更审核功能,要求要记录信息变更 修改前 和 修改后 的值。 eg: 姓名:由 张三 变更为 李四

核心注解:

import java.lang.annotation.*; @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface ColumnName { /** * 字段名称 * @return */ String value() default ""; /** * 当前字段的类型,如果是集合指定集合中的类型 * 基本类型 可不指定 * 用于复杂类型 eg: Object | List * @return */ Class classType() default void.class; /** * 是否是集合类型 * 如果是集合类型必须指定其 * 1. 集合中存放的对象类型 {@link ColumnName#classType()} * 2. 集合中的对象的唯一标识 {@link ColumnName#onlyMark()} * @return */ boolean isList() default false; /** * 如果是集合类型还要知道该集合对象中什么字段可以确定唯一 可多个 逗号隔开 eg: id,name * @return */ String onlyMark() default ""; /** * 如果是日期类型 可自定义日期格式 * 默认 yyyy-MM-dd HH:mm:ss * 常用格式,包括: * HH:mm:ss * yyyy-MM-dd * yyyy-MM-dd HH:mm * yyyy-MM-dd HH:mm:ss * yyyy-MM-dd HH:mm:ss.SSS * @return */ String dateFormat() default "yyyy-MM-dd HH:mm:ss"; }

有了以上注解后我们来看我们要对比的实体类 假设我们现在有一个人的类 里面包含 编号、姓名、年龄、生日,以及这个人养的一条狗,以及一个女朋友的List集合

人类

@Data @NoArgsConstructor @AllArgsConstructor public class Person { @ColumnName("编号") private Integer id; @ColumnName("姓名") private String name; @ColumnName("年龄") private Integer age; @ColumnName(value = "生日", dateFormat = "yyyy-MM-dd") private Date birthDay; @ColumnName(value = "狗", classType = Dog.class) private Dog dog; @ColumnName(value = "女朋友", isList= true, classType = GirlFriend.class, onlyMark = "id,age") private List girlFriends; }

狗类

@Data @NoArgsConstructor @AllArgsConstructor public class Dog { @ColumnName("狗名字") private String name; }

女朋友类

@Data @NoArgsConstructor @AllArgsConstructor public class GirlFriend { @ColumnName("编号") private Integer id; @ColumnName("姓名") private String name; @ColumnName("年龄") private Integer age; }

创建一个存放不同的类,又来存放最终对比出来的不同的结果

@Data @NoArgsConstructor @AllArgsConstructor public class Different { // 字段 private String field; // 对应的字段提示 eg: 姓名 private String fieldName; // 变更前的值 private String oldData; // 变更后的值 private String newData; @Override public String toString() { return "数据库字段:'" + field + "', 字段:'" + fieldName + '\'' + ", 老值:'" + oldData + '\'' + ", 新值:'" + newData + '\''; } }

现在我们有一个修改之前的人对象和修改之后的人对象 要求:获取到两者的不同之处

public static void main(String[] args) throws Exception { // 修改之前的对象 Person person1 = new Person(); person1.setId(1); person1.setName("张三"); person1.setBirthDay(DateUtil.parse("2018-05-05")); person1.setAge(18); Dog dog = new Dog(); dog.setName("张三的狗"); person1.setDog(dog); person1.setGirlFriends(Arrays.asList( new GirlFriend(1, "小花", 19) , new GirlFriend(2, "小丽", 20) , new GirlFriend(3, "小青", 10) )); // 修改之后的对象 Person person2 = new Person(); person2.setId(1); person2.setName("张三"); person2.setBirthDay(DateUtil.parse("2018-05-05")); person2.setAge(18); Dog dog2 = new Dog(); dog2.setName("张三的狗"); person2.setDog(dog2); person2.setGirlFriends(Arrays.asList( new GirlFriend(1, "小花", 19) , new GirlFriend(2, "小丽", 20) , new GirlFriend(3, "小青", 10) )); // 获取两个对象的不同 getDifferents(person1, person2, Person.class).forEach(System.out::println); }

我们修改 person2 的name 修改为: 张三三 并执行 Main 可以得到以下结果 在这里插入图片描述 继续修改 person2 的birthday 修改为 :2018-10-10 并执行 Main 可以得到以下结果 在这里插入图片描述 如果日期的格式不符合预期结果 可以在@ColumnName#dateFormat() 指定其日期的格式 继续修改dog2的name 为:张三的大黑狗 可以得到以下结果 在这里插入图片描述 复杂的类型需要在实体类中指定类型 eg:@ColumnName#classType() 在这里插入图片描述 我们继续修改女朋友的集合中的数据 将名字叫小花的女朋友名字修改为 花姑娘 结果 在这里插入图片描述 由于集合会出现 添加、修改、删除三种 不同的情况 所有这里再添加一个接口

public interface FieldNameHandler { /** * 增 */ String ADDED_PREFIX = "addedPrefix"; /** * 删 */ String DELETE_PREFIX = "deletePrefix"; /** * 改 */ String UPDATE_PREFIX = "updatePrefix"; /** * 处理添加字段名 * @return 添加数据时的字段名前缀 */ String addedPrefix(); /** * 处理删除字段名 * @return 删除数据时的字段名前缀 */ String deletePrefix(); /** * 处理修改字段名 * @return 修改数据时的字段名前缀 */ String updatePrefix(); }

让我们的GirlFriend 实现 FieldNameHandler 实现后我们的GirlFriend变成

@Data @NoArgsConstructor @AllArgsConstructor public class GirlFriend implements FieldNameHandler { @ColumnName("编号") private Integer id; @ColumnName("姓名") private String name; @ColumnName("年龄") private Integer age; @Override public String addedPrefix() { return StrUtil.format("添加女朋友 "); } @Override public String deletePrefix() { return StrUtil.format("删除女朋友 "); } /** * 复杂类型的 变更字段 可以自定义 * @return 返回值会加上 {@link ColumnName#value()} 的值 * 如果当前对象的姓名改了 字段的值就为 : 修改编号为:1的女朋友 */ @Override public String updatePrefix() { return StrUtil.format("修改编号为:" + this.id + "的女朋友 "); } }

我们再次运行Main 在这里插入图片描述 字段本来的值是@ColumnName#value() 的值 姓名 实现了FieldNameHandler接口后如果发现是新增、删除、修改 会自动调用 实现类中的addedPrefix、deletePrefix、updatePrefix 并 加上@ColumnName#value()

添加删除女朋友这里就不模拟了 最后贴上getDifferents 写的比较粗糙 见谅

/** * 数据修改对比统计 * * @param oldT 修改前 * @param newT 修改后 * @param className 类名 * @param * @return * @throws Exception */ public static List getDifferents(T oldT, T newT, Class className) throws IllegalAccessException, InstantiationException { // 存放处理结果 List differents = new ArrayList(); // 获取当前类的所有字段 Field[] fields = className.getDeclaredFields(); for (Field f : fields) { // 过滤 static、 final、private static final字段 if (f.getModifiers() == Modifier.FINAL || f.getModifiers() == Modifier.STATIC || f.getModifiers() == (Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL)) { continue; } // 获取当前字段注解 ColumnName annotationColumn = f.getAnnotation(ColumnName.class); if (annotationColumn == null) { continue; } // 反射获取当前老对象的字段值 Object oldV = ReflectUtil.getFieldValue(oldT, f.getName()); // 反射获取当前新对象的字段值 Object newV = ReflectUtil.getFieldValue(newT, f.getName()); // 当前字段的类型 用于复杂类型 eg: Object List Set final Class dataType = annotationColumn.classType(); final String typeName = annotationColumn.classType().getTypeName(); // 如果他是集合类型 if (annotationColumn.isList()) { // 二次校验是否是集合类型 if (Objects.isNull(oldV)) oldV = new ArrayList(); if (Objects.isNull(newV)) newV = new ArrayList(); final boolean isCollection = Collection.class.isAssignableFrom(oldV.getClass()); if (isCollection) { final List oldList = (List) oldV; final List newList = (List) newV; // 两个集合都是空的继续处理剩余字段 if (CollectionUtil.isEmpty(oldList) && CollectionUtil.isEmpty(newList)) { continue; } // 老的是空的 新的有值 说明是集合中新添加了值 并不存在修改操作 if (CollectionUtil.isEmpty(oldList) && CollectionUtil.isNotEmpty(newList)) { for (Object n : newList) { differents.addAll(getDifferents(n.getClass().newInstance(), n, dataType)); } continue; } // 新的是空的 老的有值 说明是集合中的值全部删除了 if (CollectionUtil.isNotEmpty(oldList) && CollectionUtil.isEmpty(newList)) { for (Object o : oldList) { differents.addAll(getDifferents(o, o.getClass().newInstance(), dataType)); } continue; } // 创建临时集合 List allList = new ArrayList(); allList.addAll(oldList); allList.addAll(newList); for (Object o : oldList) { int matchCount = 0; for (Object n : newList) { final String key = annotationColumn.onlyMark(); // 如果存在多个唯一标识字段特殊处理 if (key.contains(",")) { final String[] keys = key.split(","); boolean isSuccess = true; for (String s : keys) { final Object o1 = ReflectUtil.getFieldValue(o, s); final Object n1 = ReflectUtil.getFieldValue(n, s); // 有一个不匹配跳出循环 if (!o1.equals(n1)) { isSuccess = false; break; } } if (isSuccess) { // 匹配到就-- matchCount--; differents.addAll(getDifferents(o, n, dataType)); } else { // 没有匹配到就++ matchCount++; // 如果不是一个对象 就去判断该新对象是否是之前已经判断过了的对象 final long count = allList.stream().filter(e -> { for (String s : keys) { final Object o1 = ReflectUtil.getFieldValue(e, s); final Object n1 = ReflectUtil.getFieldValue(n, s); // 有一个不匹配跳出循环 if (!o1.equals(n1)) { return false; } } return true; }).count(); if (count final Object o1 = ReflectUtil.getFieldValue(o, key); final Object n1 = ReflectUtil.getFieldValue(n, key); if (o1.equals(n1)) { // 匹配到就-- matchCount--; differents.addAll(getDifferents(o, n, dataType)); } else { // 没有匹配到就++ matchCount++; // 如果不是一个对象 就去判断该新对象是否是之前已经判断过了的对象 final long count = allList.stream().filter(e -> ReflectUtil.getFieldValue(e, key).equals(ReflectUtil.getFieldValue(n, key))).count(); if (count differents.addAll(getDifferents(o, o.getClass().newInstance(), dataType)); } } } } // 不能是 void 和 基本类型 if (!"void".equals(typeName) && !ClassUtil.isBasicType(dataType)) { final Class aClass = annotationColumn.classType(); differents.addAll(getDifferents(oldV, newV, aClass)); continue; } // 检查新老对象的值是否一致 不一致记录 if (!Objects.equals(newV, oldV)) { // 字段名字 String fieldName = StrUtil.isNotEmpty(annotationColumn.value()) ? annotationColumn.value() : f.getName(); // 获取当前类所实现的所有接口 final Class[] interfaces = className.getInterfaces(); // 是否实现了FieldNameHandler接口,如果实现了FieldNameHandler接口 就追加调用 FieldNameHandler#handle() final long count = Stream.of(interfaces).filter(a -> a.equals(FieldNameHandler.class)).count(); if (count > 0) { Object invoke = null; // 添加了数据 if (oldT.getClass().newInstance().equals(oldT)) { invoke = ReflectUtil.invoke(oldT, FieldNameHandler.ADDED_PREFIX); } // 删除了数据 else if (newT.getClass().newInstance().equals(newT)) { invoke = ReflectUtil.invoke(oldT, FieldNameHandler.DELETE_PREFIX); } // 修改了数据 else { invoke = ReflectUtil.invoke(oldT, FieldNameHandler.UPDATE_PREFIX); } if (Objects.nonNull(invoke)) { fieldName = invoke + fieldName; } } // 老对象值 String oldValue = null != oldV ? oldV.toString() : ""; // 新对象值 String newValue = null != newV ? newV.toString() : ""; // 日期格式特殊处理 if (oldV instanceof Date && newV instanceof Date) { if (StrUtil.isNotEmpty(oldValue)) { oldValue = DateUtil.format((Date) oldV, annotationColumn.dateFormat()); } if (StrUtil.isNotEmpty(newValue)) { newValue = DateUtil.format((Date) newV, annotationColumn.dateFormat()); } } // 添加处理结果中 differents.add(new Different(f.getName(), fieldName, oldValue, newValue)); } } return differents; }

转载注明出处 谢谢。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有